#include "mmu.h"
#include "i386.h"

//////////////////////////////////////////////
//Variables globales para el manejo de memoria
void * usuarioLibre;
unsigned int pagUsuario;
void * kernelLibre;
unsigned int pagKernel;
/////////////////////////////////////////////

void inicializar_mmu(){
	//última página de usuario libre
	usuarioLibre = (void*)INICIO_PAGINAS_USUARIO;
	//Cantidad de páginas de usuario libre
	pagUsuario=CANTIDAD_PAGINAS_USUARIO;

	//última página de kernel libre
	kernelLibre = (void*)INICIO_PAGINAS_KERNEL;
	//Cantidad de páginas de kernel libre
	//pagKernel=CANTIDAD_PAGINAS_USUARIO;
	pagKernel=CANTIDAD_PAGINAS_KERNEL;
}


unsigned int *pagina_libre_kernel(){
	unsigned int *entrego;
	
	if(pagKernel == 0) return 0;
	
	//si no tengo más páginas devuelvo error (0)
	entrego=(unsigned int *)0;
	entrego=kernelLibre;
	if(pagKernel >= 15){
		kernelLibre+=TAMANO_PAGINA;
	} else {
		kernelLibre+=TAMANO_PAGINA_4MB;
	}
	pagKernel-- ;
		
	return entrego;
}

unsigned int *pagina_libre_usuario(){
	unsigned int *entrego;
	
	if(pagUsuario == 0) return 0;	

	//si no tengo más páginas devuelvo error (0)
	entrego=(unsigned int *)0;
	if(pagUsuario--){
		entrego=usuarioLibre;
		usuarioLibre+=TAMANO_PAGINA;
	}
	
	return entrego;	
}


unsigned int* inicializar_dir_kernel(){
		unsigned int baseK = 0;
		unsigned int i;
		
		unsigned int *dirbase =(unsigned int *) 0x00100000;		
		//Uso una pagina de kernel para el directorio de tablas de páginas
		//incremento el puntero a la última página de kernel libre
		kernelLibre+=TAMANO_PAGINA;
		pagKernel--;
		unsigned int *tablebase =(unsigned int *) 0x00101000;
		//Uso una pagina de kernel para la tabla de páginas
		//incremento el puntero a la última página de kernel libre
		kernelLibre+=TAMANO_PAGINA;
		pagKernel--;
		
		//La entrada de directorio apunta a una tabla con atributos: SUPERVISOR,LECTURA_ESCRITURA, PRESENTE
		*dirbase =((unsigned int) tablebase | SUPERVISOR|LECTURA_ESCRITURA|PRESENTE);
		////dirbase++;

		//Entradas de tabla de páginas para páginas de kernel
		for(baseK=0; baseK < DIR_4MB; baseK += TAMANO_PAGINA) {
			*tablebase =(baseK | PRESENTE|LECTURA_ESCRITURA|SUPERVISOR);
			tablebase++;
		}

		for(i=1; i<16; i++) { 
				//Entradas de tabla de páginas para páginas de 4 megas del kernel
				// extender_dir_kernel
				//La entrada de directorio apunta a una tabla con atributos: SUPERVISOR,LECTURA_ESCRITURA, PRESENTE
				dirbase[i] = ((unsigned int) (i*TAMANO_PAGINA_4MB) | SIZE_PAGE_4MB | SUPERVISOR|LECTURA_ESCRITURA|PRESENTE);
		}

		
		for(i=16;i<0x400;i++){
			//inicializo las entradas restantes del directorio. 
			//Si la entrada está NO_PRESENTE la tabla de páginas no existe y hay que crearla
			dirbase[i] =((unsigned int) 0 | NO_PRESENTE);
		}
		
		
		
		//devuelvo la base del directorio de páginas
		return (unsigned int*)0x00100000;
}


unsigned int* inicializar_dir_usuario(){
		
		unsigned int baseU = 0;
		
		//pido una página para el directorio de tablas de páginas
		unsigned int *dirbase = pagina_libre_kernel();
		
		//pido una página para la tabla de páginas
		unsigned int *tablebase = pagina_libre_kernel();
		

		//uso solo una entrada de directorio porque una tabla me basta para mapear las direcciones que se piden
		*dirbase =((unsigned int) tablebase) | SUPERVISOR|LECTURA_ESCRITURA|PRESENTE;
		
		//Entradas de tabla de páginas para páginas de usuario
		for(baseU=0; baseU < 0x000fffff; baseU += TAMANO_PAGINA) {
			*tablebase =(baseU | PRESENTE|LECTURA_ESCRITURA|SUPERVISOR);
			tablebase++;
		}
		
		//devuelvo la base del directorio de páginas
		return dirbase;
}

void mapear_pagina(unsigned int virtual, unsigned int cr3, unsigned int fisica, unsigned int atributos) {
	
	unsigned int PDI;
	unsigned int PTI;
	
		 
	PDI = virtual>>22;
	PTI = virtual>>12 & 0x3ff;
	
	atributos = atributos & 0xfff;		// Por si tiene basura en la parte alta
	
	cr3 = cr3 & ~0xfff;
	unsigned int* dirEntry = (unsigned int *)(cr3 + 4*PDI);
	
	
	//Si no está presente la tabla pido espacio y la ubico
	if(!(*dirEntry & PRESENTE)){
		*dirEntry = (unsigned int) pagina_libre_kernel();
		//*dirEntry= *dirEntry|PRESENTE|LECTURA_ESCRITURA|SUPERVISOR;
		*dirEntry= *dirEntry|atributos;
	}
	unsigned int* tableEntry = (unsigned int *) ((*dirEntry&~0x3ff)+PTI*4);
	
	fisica = fisica & ~0x3ff;
	//*tableEntry = (unsigned int)(fisica |SUPERVISOR|LECTURA_ESCRITURA|PRESENTE);
	*tableEntry = (unsigned int)(fisica | atributos);
	
	//en el enunciado recomiendan usar esta func. despues de modificar la tabla o directorio de paginas
	tlbflush();
    return;
}

void unmapear_pagina(unsigned int virtual, unsigned int cr3) {
    unsigned int PDI;
	unsigned int PTI;
	
		 
	PDI = virtual>>22;
	PTI = virtual>>12 & 0x3ff;
	
	cr3 = cr3 &  ~0xFFF;
	unsigned int* dirEntry = (unsigned int *)(cr3 + 4*PDI); 
	
	
	//Si no está presente la tabla no tiene sentido desmapear
	if(!(*dirEntry & PRESENTE))
		return;
	
	unsigned int* tableEntry = (unsigned int *) ((*dirEntry&~0x3ff)+PTI*4);
	
	//seteo como no presente a la entrada por ende ya no se mapea la dirección virtual
	*tableEntry = *tableEntry & ~PRESENTE;
	//en el enunciado recomiendan usar esta func. despues de modificar la tabla o directorio de paginas
	tlbflush();
	
    return;
}

